package a2;

/**
Tests a parser for the grammar with start symbol Program
and productions
<pre>
Program -> {VariableDefinition} {FunctionDefinition} Expr
VariableDefinition -> let Identifier Expr 
FunctionDefinition -> define Signature Program 
Signature -> Identifier ( {Identifier} )
Expr -> StringLiteral | Identifier | Application
Application -> apply FunctionName ( {Expr} )
FunctionName -> Identifier | + | ~
</pre>

In these productions, the asterisk is a metasymbol,
while the parentheses and brackets
(as well as <code>+</code> and <code>*</code> and <code>-</code>)
are terminal symbols of the grammar.
Also,
<code>Identifier</code> is a preterminal whose corresponding terminal
must start with a capital letter,  and
@author Jeff Smith
@version for Assignment 1, CS 152, SJSU, Fall 2011 
 */
public class A2
{

    /**
    Takes an input string purporting to represent a program in 
    L(G), where G is the context-free grammar defined above, and
    prints an appropriately indented representation of a syntax
    tree for the string if it is in L(G).
    Otherwise prints an informative error message stating
    why the string is not in the language.
    @param expr the string, represented as a string of tokens 
    separated by whitespace characters as recognized by the 
    <code>Character.isWhitespace</code> predicate.
     */
    private static void test(String program)
    {
        try
        {
            System.out.println((new Parser()).parse(program));
        }
        catch (IllegalArgumentException e)
        {
            System.out.println(e.getMessage() + "\n");
        }
    }

    /**
    The main test program
    @param args is ignored
     */
    public static void main(String[] args)
    {
        int x = 0;
        String newline = System.getProperty("line.separator");
         test("abd");
         test("apply + ( ab d )");
         test("apply ~ ( apply ~ ( abc ) )");
         test("apply ~ ( apply ~ ( apply ~ ( abc ) ) )");
         test("let X abc apply + ( X d )");
         test("let X abc " + newline + "apply + ( X d )");
         test("let X abc let Y def apply + ( X  Y )");
         test("define F ( X ) apply + ( X d ) apply  F ( abc )");
         test("define F ( X1 X2 ) apply + ( X1 apply ~ ( X2 ) ) apply  F ( abc def )");
         test("define F ( ) d apply  F ( )");
         test("let E e define F ( X ) apply + ( X  E ) apply  F ( abc )");
         test("let E e let ABC abc define F ( X ) apply + ( X  E ) "
                + "apply  F ( ABC )");
         test("let Q pq define Q ( W ) apply + ( W Q ) apply Q ( apply ~ ( Q ) )");
         test("define F ( X ) "
                + "define F ( X ) apply + ( e X ) "
                + "apply + ( X d ) "
                + "apply  F ( apply F ( abc ) )");
         test("let X efg "
                + "define F ( W ) "
                + "let Y apply + ( X W ) "
                + "apply + ( x Y ) "
                + "apply ~ ( apply F ( abc ) )");
         test("let X efg "
                + "define F ( W ) "
                + "let X W "
                + "let Y apply + ( X W ) "
                + "apply + ( x Y ) "
                + "apply ~ ( apply F ( abc ) )");
         test("define F ( W ) "
                + "let X W "
                + "let Y apply + ( X W ) "
                + "apply + ( x Y ) "
                + "apply ~ ( apply F ( abc ) )");
         test("define F ( X ) apply + ( X d ) "
                + "define G ( X ) apply + ( e X ) "
                + "apply F ( apply G ( abc ) )");
         test("define H ( W ) "
                + "define F ( X ) apply ~ ( X ) "
                + "define G ( X ) apply + ( q apply F ( X ) ) "
                + "apply + ( apply F ( W ) apply G ( W ) ) "
                + "apply H ( abc )");
         test("define F ( X ) apply ~ ( apply + ( X  d ) ) "
                + "apply F ( abc )");
         test("define F ( X ) apply + ( X  u ) "
                + "define G ( XX ) "
                + "define F ( X ) apply + ( X v ) "
                + "apply ~ ( apply F ( XX ) ) "
                + "apply G ( abc )");
        System.out.println();

         test(null);
         test("");
         test("3");
         test("apply + ab d )");
         test("apply ( ab d )");
         test("apply a ( b d )");
         test("let abc d");
         test("let X abc");
         test("apply + ( ab d ");
         test("X abc X");
         test("F ( X ) apply + ( X  d ) apply  F ( abc )");
         test("define FF ( X ) apply + ( X  d ) return apply  FF ( abc )");
         test("let X abc d )");
         test("let X abc d e");
         test("let X abc Y d apply + ( X Y )");
         test("let X define F ( X ) ~ ( X ) end F ( X )");
         test("define F0 ( X ) ( Y ) apply + ( X  Y ) apply  F6 ( abc )");
         test("define F1 apply + ( X  d ) apply  F1 ( abc )");
         test("define F2 ( q ) apply + ( X  d ) apply  F2 ( abc )");
         test("define F3 [ X ] apply + ( X  d ) apply  F3 ( abc )");
         test("define F4  X ) apply + ( X  d ) apply  F4 ( abc )");
         test("define F5 ( X ( apply + ( X  d ) apply  F5 ( abc )");
         test("define F6 ( X  apply + ( X  d ) apply  F6 ( abc )");
         test("define F7 ( + ) d  apply F7 ( abc )");
         test("define F8 ( X ) apply + ( X  d ) apply  F ( abc ) d8");
         test("let X abc apply + ( X  X ) let Y def ");
         test("define F ( X ) apply + ( X  d ) F ( abc )");
         test("define + ( X ) apply + ( X  d ) apply  F ( abc )");
         test("define f ( X ) apply + ( X  d ) apply  F ( abc )");
         test("define ( X ) apply + ( X  d ) apply  F ( abc )");
         test("define F ( X ) + ( X  d ) apply  F ( abc )");
         test("define F ( X ) apply + ( X  E ) let E e apply F ( 'abc )");
         test("let W pq define F ( X ) apply + ( X  E ) let E e apply F ( 'abc )");
         test("let X abc apply - ( X  d )");
         test("define F ( X ) apply F ( abc )");
         test("define F ( X ) apply ~ ( apply + ( X  d )  "
                + "apply F ( abc )");
         test("define F ( X ) apply ~ ( apply + ( X  d ) ) ) "
                + "apply F ( abc )");
        System.out.println();

         test("X");
         test("let X abc apply + ( X  Y )");
         test("apply F ( xyz ) ");
         test("define F ( X ) apply + ( X  d ) F");
         test("define F ( X ) apply + ( QQ  d ) apply F ( efg )");
         test("define F ( X ) apply X ( d ) apply F ( efg )");
         test("define F ( X ) apply + ( X  d ) apply  G ( abc )");
         test("define F ( X X ) apply + ( X  d ) apply  F ( a bc )");
         test("let X ad let Y be let X cf apply + ( X  Y )");
         test("define F ( X ) apply + ( X d ) "
                + "define G ( X ) apply + ( e X ) "
                + "define F ( X Y ) apply + ( Y X ) "
                + "apply F ( apply G ( abc ) )");
         test("apply ~ ( )");
         test("apply + ( z )");
         test("apply ~ ( x y )");
         test("apply + ( x y z )");
         test("define F ( X ) apply + ( X  d ) apply  F ( a bc )");
         test("define F ( W ) "
                + "let X X "
                + "let Y apply + ( X W ) "
                + "apply + ( x Y ) "
                + "apply ~ ( apply F ( abc ) )");
         test("define F ( W ) "
                + "let Y apply + ( X W ) "
                + "let X W "
                + "apply + ( x Y ) "
                + "apply ~ ( apply F ( abc ) )");
         test("define H ( W ) "
                + "define G ( X ) apply + ( q apply F ( X ) ) "
                + "define F ( X ) apply ~ ( X ) "
                + "apply + ( apply F ( W ) apply G ( W ) ) "
                + "apply H ( abc )");
        System.out.println();

         test(" define F ( U W ) "
                + "   define H ( VV ) apply + ( VV W ) "
                + "   define G ( W ) apply H ( W ) "
                + "   apply + ( apply G ( U ) apply H ( W ) ) "
                + " apply F ( x1 x30 )");
         test(" define F ( U ) "
                + "   define K ( X1 ) apply + ( y20 X1 ) "
                + "   define H ( VV ) apply K ( VV ) "
                + "   define G ( W ) "
                + "      define K ( WW ) apply + ( WW y10 ) "
                + "      apply H ( apply + ( W y1 ) ) "
                + "   apply G ( apply + ( U y2 ) ) "
                + " apply F ( y1 )");
         test(" define F ( X ) "
                + "   define P ( Y ) "
                + "     define H ( Y ) apply + ( X Y )"
                + "     define G ( X ) apply H ( apply + ( z1000 X ) ) "
                + "     apply G ( apply + ( z200 Y ) ) "
                + "   define Q ( X ) apply P ( apply + ( z100 X ) ) "
                + "   apply Q ( apply + ( z10 X ) ) "
                + " apply F ( z1 )");

        // x1x30x30x30 under static scoping
        // x1x1x30x30 under dynamic scoping
         test(" define F ( U W ) "
                + "   define H ( VV ) apply + ( VV W ) "
                + "   define G ( W ) apply H ( W ) "
                + "   apply + ( apply G ( U ) apply H ( W ) ) "
                + " apply F ( x1 x30 )");

        // y20y1y2y1 under static scoping
        // y1y2y1y10 under dynamic scoping     
         test(" define F ( U ) "
                + "   define K ( X1 ) apply + ( y20 X1 ) "
                + "   define H ( VV ) apply K ( VV ) "
                + "   define G ( W ) "
                + "      define K ( WW ) apply + ( WW y10 ) "
                + "      apply H ( apply + ( W y1 ) ) "
                + "   apply G ( apply + ( U y2 ) ) "
                + " apply F ( y1 )");

        // z1z1000z200z100z10z1 under static scoping     
         test(" define F ( X ) "
                + "   define P ( Y ) "
                + "     define H ( Y ) apply + ( X Y )"
                + "     define G ( X ) apply H ( apply + ( z1000 X ) ) "
                + "     apply G ( apply + ( z200 Y ) ) "
                + "   define Q ( X ) apply P ( apply + ( z100 X ) ) "
                + "   apply Q ( apply + ( z10 X ) ) "
                + " apply F ( z1 )");
    }
}
